package com.globant.ErrorManager;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import org.springframework.dao.DataAccessException;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;

import com.globant.AutomateEMR.AutomateImport;
import com.globant.AutomateEMR.FileManager;
import com.globant.AutomateEMR.NewLogsNotifier;
import com.globant.AutomateEMR.PropertyLoader;
import com.globant.Exceptions.ErrorManagerException;
import com.globant.util.email.EmailSender;
import com.globant.util.log.ILogFunctions;

/**
 * Handles and processes the application errors.
 * 
 * @author leandro.mora, gonzalo.zarza
 */

public class ErrorMang {
    
    /* 
    ================================================================================================
     Fields
    ================================================================================================
    */
    private AnswerToError           errorHandler;
    private EmailSender             sender;
    private SimpleMailMessage       message;
    
    public final static String      STATE_NO_ERRORS     = "No errors";
    public final static String      STATE_ERRORS_FOUND  = "Error Founded";
    
    
    /* 
    ================================================================================================
     Methods
    ================================================================================================
    */  
    /**
     * Extended constructor. Sets the local sender and message from the main beans.
     * 
     * @param prop          property file object
     * @param sender        sender bean object
     * @param message       message bean object
     */
    public ErrorMang(PropertyLoader prop, EmailSender sender, SimpleMailMessage message) {
        // new code: set the beans needed to send the error/exception notification mails
        this.sender       = sender;
        this.message      = message;
    }
    
    /* ------------------------------------------------------------------------------------------ */
    
    public void automateErrorManager(PropertyLoader pLoader, ILogFunctions pLogs, String pLogId,File logFile) throws ErrorManagerException {
        
        try{        
        	AutomateImport   autImp   = new AutomateImport(pLoader);
            AnswerToError    firstAns = new AnswerToError(pLoader);
            NewLogsNotifier  notifier = new NewLogsNotifier();
            String           path;
            int              counter;

            this.setErrorHandler(firstAns);         
            //We have to create Directories because an s3fs limitation
            path     = pLoader.readProperty("PathToBucket")+ pLoader.readProperty("PathToProcessedLogs");
            FileManager.createDirectories(new File(path));
            path     = pLoader.readProperty("PathToBucket") + pLoader.readProperty("PathToExtraInformation");
            FileManager.createExtraDirectories(new File(path));
            
            counter  = Integer.parseInt(pLoader.readProperty("MinimunAmoutOfLogsAccepted"));
            this.manageError(pLoader, counter, autImp, notifier, pLogs, pLogId,logFile);
            
        } catch(Exception e) {
            e.printStackTrace();
            throw new ErrorManagerException(e.getClass().getName()); 
        }
    }
    
    /* ------------------------------------------------------------------------------------------ */
    
    // TODO test
    public void manageError(PropertyLoader pLoader, int pMinCounter, AutomateImport pAutImp, NewLogsNotifier pNotifier, ILogFunctions pLogs, String pLogId,File logFile) throws ErrorManagerException, MailException {
        
        System.out.println("Starting...");
        FileManager fmanager	=new FileManager();        
        boolean     errorFound  = false;
        ErrorLog    errorLog    = new ErrorLog();
        String      newWords    = null;
        String[]    folders     = {"akn_traffic_fact/", "akn_session_fact/", "akn_query_fact/",
                                         "akn_word_fact/", "akn_noresultquery_fact/"};
        try {           
            errorFound          = this.manageReports(pLoader, pMinCounter, folders, errorLog, pLogs, pLogId);
            
            if(errorFound){
    			String pathFromLogs			=pLoader.readProperty("PathToBucket")+pLoader.readProperty("PathToInputProcessedLogs");
    			String pathToMoveErrorLog	=pLoader.readProperty("PathToBucket")+pLoader.readProperty("PathToNotStoredLogs");
    			errorLog					= pAutImp.storeErrorLog(errorLog,fmanager,pathFromLogs,pathToMoveErrorLog,logFile);         
                String error_message="We have found problems while processis the following report:"+errorLog.getErrorReports().toString()+".Go to the following S3 location :"+errorLog.getPathToS3()+" to get the following log:"+errorLog.getPathToS3();
    			error_message="\n";
    			error_message="For more information please see the Error Manual.";
                this.notifyErrors("Error found in the reports",error_message);        
            }else{     
				String pathFromLogs=pLoader.readProperty("PathToBucket")+pLoader.readProperty("PathToInputProcessedLogs");           	
				String pathToLogs=pLoader.readProperty("PathToBucket")+pLoader.readProperty("PathToStoredLogs");
				System.out.println(logFile.getAbsolutePath());
				pAutImp.storeEffectiveLog(fmanager,pathFromLogs,pathToLogs,logFile);
	            newWords        = pNotifier.getNewWordsFromLogs(pLoader);                
	            if(newWords != null){	
	            	this.notifyErrors("New Words found", newWords);
	            }
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new ErrorManagerException(e.getClass().getName());
        } catch (DataAccessException dae){
            dae.printStackTrace();
            throw new ErrorManagerException(dae.getClass().getName());
        }
    }

    /* ------------------------------------------------------------------------------------------ */
    
    // TODO test
    public boolean manageReports(PropertyLoader pLoader, int minimun_Counter, String[] folders, ErrorLog errorLog, ILogFunctions pLogs, String pLogId) throws ErrorManagerException, IOException, DataAccessException {
        
        String  path        = pLoader.readProperty("PathToBucket") + pLoader.readProperty("PathToProcessedLogs");
        String  status      = ErrorMang.STATE_NO_ERRORS;
        boolean errors      = false;
        int     reportId    = 0;
        int     statusCode  = 0;
        String  reportName  = null;
        
        for(int i = 0; i < folders.length; i++){            
            File file       = new File(path + folders[i]);
            status          = checkError1234(file, minimun_Counter, ErrorMang.STATE_NO_ERRORS);
            
            if (!status.equals(ErrorMang.STATE_NO_ERRORS)){
                status      = checkError1234(file, minimun_Counter, status);
            }

            if (!status.equals(ErrorMang.STATE_NO_ERRORS)){
                file.delete();
                errorLog.getErrorReports().add(folders[i]);
                errors      = true;
            }  
            
            // get the parameters needed
            if (status.equals(ErrorMang.STATE_NO_ERRORS)){
                statusCode  = ILogFunctions.SQL_STATUS_COMPLETED;
            } else if (status.equals(ErrorMang.STATE_ERRORS_FOUND)){
                statusCode  = ILogFunctions.SQL_STATUS_FAILED;
            } else {
                statusCode  = ILogFunctions.SQL_STATUS_PENDING;
            }    
            
            reportName      = folders[i];
            
            System.out.println("reportName: " + reportName);
            
            if (reportName.equals(ILogFunctions.REPORT_NAME_TRAFFIC)){
                reportId    = 1;
            } else if (reportName.equals(ILogFunctions.REPORT_NAME_SESSION)){
                reportId    = 2;
            } else if (reportName.equals(ILogFunctions.REPORT_NAME_WORD)){
                reportId    = 3;
            } else if (reportName.equals(ILogFunctions.REPORT_NAME_QUERY)){
                reportId    = 4;
            } else if (reportName.equals(ILogFunctions.REPORT_NAME_NO_RESULT)){
                reportId    = 5;
            } else {
                throw new IOException("Invalid report name");
            }
            // update the report status
            pLogs.updateReportStatusInformation(pLogId, reportId, statusCode);

        }
        
        return (errors);
    }
    
    /* ------------------------------------------------------------------------------------------ */

    /**
     * Error notification method: sends an email to the corresponding address. 
     * 
     * @param pErrSubject      the subject of the error email
     * @param pErrMessage      the text of the error email
     * @throws MailException   if cannot complete the operation
     */
    public void notifyErrors(String pErrSubject, String pErrMessage) throws MailException {                            
        this.message.setSubject(pErrSubject);
        this.message.setText(pErrMessage);
        this.sender.sendMail(this.message);        
    }
    
    
    /* 
    ================================================================================================
     Error Detective
    ================================================================================================
    */
    public String checkError1234(File reportDirectory, int minimun_Counter, String pState) throws ErrorManagerException {
        
        String status = pState;
        
        if(reportDirectory.isDirectory()){
            status     = checkError23(reportDirectory, minimun_Counter, pState);
        } else {
            System.out.println("Error: the folder: " + reportDirectory.getName() + " doesn't exist (1)");            
            if (status.equals(ErrorMang.STATE_NO_ERRORS)){
                errorHandler.answer("1",reportDirectory);
            }           
            status = ErrorMang.STATE_ERRORS_FOUND;
        }        
        return (status);        
    }
    
    /* ------------------------------------------------------------------------------------------ */
    
    public String checkError23(File reportDirectory,int minimun_Counter, String pState) throws ErrorManagerException {
        
        File[] files   = reportDirectory.listFiles();
        String status  = pState;
        
        if(files.length > 0){
            status     = checkError3(reportDirectory, pState);            
            if(status.equals(ErrorMang.STATE_NO_ERRORS)){
                status = checkError4(reportDirectory, minimun_Counter, status);
            }            
        } else {
            System.out.println("Error: The directory: "+reportDirectory.getName()+" is empty (2)");
            if(status.equals(ErrorMang.STATE_NO_ERRORS)){
                errorHandler.answer("2",reportDirectory);
            }
            status   = ErrorMang.STATE_ERRORS_FOUND;
        }        
        return (status);
    }
    
    /* ------------------------------------------------------------------------------------------ */
    
    public String checkError3(File reportDirectory, String pState) throws ErrorManagerException {
        File      files[] = reportDirectory.listFiles();
        String    status  = pState;
        
        if(files.length < 3){
            System.out.println("Error: the application couldn't process the "+reportDirectory.getName()+" (3)");
            if(status.equals(ErrorMang.STATE_NO_ERRORS)){
                errorHandler.answer("3",reportDirectory);
            }
            status = ErrorMang.STATE_ERRORS_FOUND;
        }        
        return (status);
    }
    
    /* ------------------------------------------------------------------------------------------ */
    
    public String checkError4(File reportDirectory, int minimun_Counter, String pState)throws ErrorManagerException{
        File      files[] = reportDirectory.listFiles();
        String    status  = pState;
        int       counter = 0;
        
        try {            
            counter    = countAmountOfLogs(files, counter);     
            System.out.println("COUNTER IS:"+counter);
            
            if(counter < minimun_Counter){
                System.out.println("The amount of information processed is not sufficient. Report Directory"+reportDirectory+" (4)");
                if(status.equals(ErrorMang.STATE_NO_ERRORS)){
                    errorHandler.answer("4",reportDirectory);
                }
                status  = ErrorMang.STATE_ERRORS_FOUND;                
            } else {
                status = ErrorMang.STATE_NO_ERRORS;
                System.out.println("Perfect Log");
                /*
                Log no-errors "Status: Ready to Store". 
                This will be done at the highest-level, in the automateBDProcessing(...) in the 
                AutomateBigDataProcessing class
                */
            }            
        } catch (Exception e) {     
            e.printStackTrace();
            System.out.println("An unexpected error occurred, leave a message and send a email. Report Directory"+reportDirectory);
            throw new ErrorManagerException();
        }        
        return (status);
    }

    /* ------------------------------------------------------------------------------------------ */
    
    private int countAmountOfLogs(File[] files, int counter) throws FileNotFoundException, IOException {
        for (int f = 0; f <files.length; f++) {
            File file=files[f];
            if(!file.getName().equals("part-r-00000") & !file.getName().equals("_SUCCESS")){
                counter = countLine(counter, file);
            }
        }
        return counter;
    }
    
    /* ------------------------------------------------------------------------------------------ */

    private int countLine(int counter, File file) throws FileNotFoundException, IOException {
        BufferedReader br;
        br = new BufferedReader(new FileReader(file));
        while ((br.readLine()) != null) {
            counter=counter+1;
        }
        br.close(); 
        return counter;
    }
    
    /* 
    ================================================================================================
     Getters and Setters
    ================================================================================================
    */

    public AnswerToError getErrorHandler() { return errorHandler; }
    public void setErrorHandler(AnswerToError errorHandler) { this.errorHandler = errorHandler; }

}
